﻿using System;
using System.Linq;
using System.Linq.Expressions;
using Moq;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Portfolios.BL.Validators;
using Sustainalytics.Utils;
using Xunit;

namespace Sustainalytics.Portfolios.BL.Tests.UnitTest
{
    [Trait("Category", "Unit")]
    public class PortfolioValidatorTests
    {
        private readonly Guid _portfolioGuid = new Guid("00000000-0000-0000-0000-000000000001");

        private readonly PortfolioUserClaims _creator = new PortfolioUserClaims
        {
            UserName = "ileana.popescu@sustainalytics.com",
            UserFullName = "Ileana Popescu",
            UserId = new Guid("4387c2a7-1eda-11e4-92ce-6c3be5be9f98"),
            AccountId = new Guid("528ea769-2d17-e411-92ce-6c3be5be9f98")
        };

        private readonly PortfolioUserClaims _sameAccountReader = new PortfolioUserClaims
        {
            UserName = "teodor.bajan@sustainalytics.com",
            UserFullName = "Teodor Bajan",
            UserId = new Guid("4387c2a7-1eda-11e4-92ce-6c3be5be9f88"),
            AccountId = new Guid("528ea769-2d17-e411-92ce-6c3be5be9f98")
        };

        private readonly PortfolioUserClaims _anotherAccountReader = new PortfolioUserClaims
        {
            UserName = "teodor.bajan@sustainalytics.com",
            UserFullName = "Teodor Bajan",
            UserId = new Guid("4387c2a7-1eda-11e4-92ce-6c3be5be9f88"),
            AccountId = new Guid("528ea769-2d17-e411-92ce-6c3be5be9f88")
        };


        public class Given_A_SharedPortfolio : PortfolioValidatorTests
        {
            private readonly Portfolio portfolio;

            public Given_A_SharedPortfolio()
            {
                this.portfolio = PortfolioBuilder.Build(
                    this._portfolioGuid,
                    "Test",
                    PortfolioSharingStatus.Shared,
                    DateTime.Today,
                    this._creator);
            }

            [Fact]
            public void Read_Portfolio_Shared_Status_Added_By_Same_User_Should_Return_True()
            {
                var actual = PortfolioValidator.Read().IsValid(this.portfolio, this._creator);
                Assert.True(actual);
            }

            [Fact]
            public void Read_Portfolio_Shared_Status_Added_By_Another_User_Same_Account_Should_Return_True()
            {
                var actual = PortfolioValidator.Read().IsValid(this.portfolio, this._sameAccountReader);
                Assert.True(actual);
            }

            [Fact]
            public void Read_Portfolio_Shared_Status_Added_By_Another_Account_Should_Return_False()
            {
                var actualResult = PortfolioValidator.Read().IsValid(portfolio, _anotherAccountReader);
                Assert.False(actualResult);
            }

            [Fact]
            public void Write_Portfolio_Added_By_Same_User_Should_Return_True()
            {
                var actualResult = PortfolioValidator.Write().IsValid(this.portfolio, this._creator);
                Assert.True(actualResult);
            }

            [Fact]
            public void Write_Portfolio_Added_By_Another_User_Should_Return_False()
            {
                var actualResult = PortfolioValidator.Write().IsValid(this.portfolio, this._anotherAccountReader);
                Assert.False(actualResult);
            }
        }

        public class Given_An_Automatic_SystemPortfolio : PortfolioValidatorTests
        {
            private readonly Portfolio portfolio;

            public Given_An_Automatic_SystemPortfolio()
            {
                this.portfolio = new AutomaticSystemPortfolio(this._portfolioGuid, "test", new[] { this._creator.AccountId });
            }

            [Fact]
            public void Read_Portfolio_Should_Return_True()
            {
                var actual = PortfolioValidator.Read().IsValid(this.portfolio, this._creator);
                Assert.True(actual);
            }
        }


        public class Given_A_Manual_SystemPortfolio : PortfolioValidatorTests
        {
            private readonly Portfolio portfolio;

            public Given_A_Manual_SystemPortfolio()
            {
                this.portfolio = new ManualSystemPortfolio("test", this._creator.AccountId, this._creator.UserId, this._creator.UserName, this._creator.UserFullName);
            }

            [Fact]
            public void Read_Portfolio_Added_By_Same_User_Should_Return_True()
            {
                var actual = PortfolioValidator.Read().IsValid(this.portfolio, this._creator);
                Assert.True(actual);
            }

            [Fact]
            public void Read_Portfolio_Added_By_Another_User_Same_Account_Should_Return_False()
            {
                var actual = PortfolioValidator.Read().IsValid(this.portfolio, this._sameAccountReader);
                Assert.False(actual);
            }
        }



        [Fact]
        public void Read_Portfolio_Private_Status_Added_By_Same_User_Should_Return_True()
        {

            var portfolio = PortfolioBuilder.Build(_portfolioGuid, "Test", PortfolioSharingStatus.Private,
                DateTime.Today, _creator);
            var actual = PortfolioValidator.Read().IsValid(portfolio, _creator);
            Assert.True(actual);
        }


        [Fact]
        public void Read_Portfolio_Private_Status_Added_By_Another_User_Should_Return_False()
        {

            var portfolio = PortfolioBuilder.Build(_portfolioGuid, "Test", PortfolioSharingStatus.Private,
                DateTime.Today, _creator);
            var actual = PortfolioValidator.Read().IsValid(portfolio, _sameAccountReader);
            Assert.False(actual);
        }


        [Fact]
        public void PortfolioNameValidator_When_In_Db_Exists_Same_Name_by_same_User_returns_false()
        {
            var portfolio = PortfolioBuilder.Build(_portfolioGuid, "test1", PortfolioSharingStatus.Shared, _creator);

            var portfolioList = new[]
            {
                portfolio,
                PortfolioBuilder.Build(Guid.NewGuid(),"test",PortfolioSharingStatus.Shared, _creator)
            };
            Mock<IRepositoryCollection<Portfolio>> portfolioRepositoryMock = new Mock<IRepositoryCollection<Portfolio>>();
            portfolioRepositoryMock.Setup(x => x.ReadWhere(It.IsAny<Expression<Func<Portfolio, bool>>>()
                , It.IsAny<int?>()
                , It.IsAny<int?>()
                , It.IsAny<Ordering<Portfolio>>()
                , It.IsAny<Expression<Func<Portfolio, object>>[]>())).Returns(
                (Expression<Func<Portfolio, bool>> exp, int? t, int? k, Ordering<Portfolio> o, Expression<Func<Portfolio, object>>[] s) =>
                portfolioList.Where(exp.Compile()).ToList());

            var actualresult = PortfolioValidator.NameValidator(portfolioRepositoryMock.Object,
                _creator).IsValid(portfolio, "test");

            Assert.False(actualresult);

        }
        [Fact]
        public void PortfolioNameValidator_When_In_Db_Exists_Same_Name_by_Another_User_returns_true()
        {
            var portfolio = PortfolioBuilder.Build(_portfolioGuid, "test1", PortfolioSharingStatus.Shared, _creator);

            var portfolioList = new[]
            {
                portfolio,
                PortfolioBuilder.Build(Guid.NewGuid(),"test",PortfolioSharingStatus.Shared, _anotherAccountReader)
            };
            Mock<IRepositoryCollection<Portfolio>> portfolioRepositoryMock = new Mock<IRepositoryCollection<Portfolio>>();
            portfolioRepositoryMock.Setup(x => x.ReadWhere(It.IsAny<Expression<Func<Portfolio, bool>>>()
                , It.IsAny<int?>()
                , It.IsAny<int?>()
                , It.IsAny<Ordering<Portfolio>>()
                , It.IsAny<Expression<Func<Portfolio, object>>[]>())).Returns(
                (Expression<Func<Portfolio, bool>> exp, int? t, int? k, Ordering<Portfolio> o, Expression<Func<Portfolio, object>>[] s) =>
                portfolioList.Where(exp.Compile()).ToList());

            var actualresult = PortfolioValidator.NameValidator(portfolioRepositoryMock.Object,
                _creator).IsValid(portfolio, "test");

            Assert.True(actualresult);

        }
    }
}
